from contextlib import asynccontextmanager

from asgi_correlation_id import CorrelationIdMiddleware
from fastapi import Depends, FastAPI
from fastapi.security import HTTPBearer

from fastapi_template.config import app_config
from fastapi_template.consts import APP_DESCRIPTION, APP_NAME
from fastapi_template.containers import Container
from fastapi_template.custom_logging import logging_middleware, setup_logging
from fastapi_template.models import init_models
from fastapi_template.services.cache_service import init_cache
from fastapi_template.webapp.auth import HandleAuthFailedMiddleware
from fastapi_template.webapp.handle_exception import app_exception_handlers

__all__ = ["create_app"]

setup_logging(json_logs=not app_config.IS_SAFE_ENV, log_level="INFO")


@asynccontextmanager
async def lifespan(app: FastAPI):
    await init_models()
    init_cache()
    yield


def create_app():
    container = Container()

    app = FastAPI(
        title=APP_NAME,
        version="0.0.1",
        docs_url=f"/{APP_NAME}/swagger-ui" if app_config.IS_SAFE_ENV else None,
        openapi_url=f"/{APP_NAME}/openapi-json"
        if app_config.IS_SAFE_ENV else None,
        description=APP_DESCRIPTION,
        dependencies=[
            # 声明这个依赖, swagger-ui上就会展示 HTTP Bearer Token Header 输入框
            Depends(HTTPBearer(auto_error=False)),
        ],
        exception_handlers=app_exception_handlers,
        lifespan=lifespan,
    )
    app.openapi_version = "3.0.3"
    setattr(app, "container", container)

    from fastapi_template.common import docs

    if app_config.IS_SAFE_ENV:
        docs.setup(
            fastapi_app=app,
            doc_ui_url=f"/{APP_NAME}/docs",
            stoplight_url=f"/{APP_NAME}/stoplight",
            rapi_url=f"/{APP_NAME}/rapi",
        )

    app.add_middleware(HandleAuthFailedMiddleware)
    app.middleware("http")(logging_middleware)

    # This middleware must be placed after the logging, to populate the context with the request ID
    # NOTE: Why last??
    # Answer: middlewares are applied in the reverse order of when they are added (you can verify this
    # by debugging `app.middleware_stack` and recursively drilling down the `app` property).
    app.add_middleware(CorrelationIdMiddleware)

    from fastapi_template.apis import root_api_router

    app.include_router(root_api_router)

    return app
